/*************************************************************************
 * Filename:    IPCManager.h
 * Description: 
 * Group 2:     nachOS
 * Authors:     Ying Wang
 *              Daniel Fairweather
 * Class:       CSE 325
 * Instructor:  Mr. Duggan
 * Assignment:  Lab #4 Interprocess Communication
 * Assigned:    March 5, 2013
 * Due:         March 19, 2013
 *************************************************************************/

#include "IPCManager.h"


int initializedMailboxes[10];
freelist fl;


void initalizeMailboxes(int mailboxesToInit[10]){
    int i;  
    fl.length = FREE_SPACE;
    
    /* Link the reserved message blocks together, forming the freelist linked list. */
    for(i = 0; i < FREE_SPACE - 1; i++){
        fl.list[i].next = &fl.list[i+1];
    }
    fl.list[i].next = NULL;
    
    fl.head = &fl.list[0];               /* Assign the head pointer of the freelist. */
    fl.tail = &fl.list[FREE_SPACE-1];    /* Assign the tail pointer of the freelist. */
    
    /* Set all head and tail pointers to NULL. */
    for(i = 0; i < 10; i++){
        mailboxes[i].head = NULL;
        mailboxes[i].tail = NULL;
        mailboxes[i].last_message.destination = -1;
        mailboxes[i].last_message.source = -1;
        mailboxes[i].last_message.message[0] = '\0';
        if(mailboxesToInit[i] == 1){
            initializedMailboxes[i] = 1;
        }
        else{
            initializedMailboxes[i] = 0;
        }
    }
}



message* enqueue(int mid, message* message){
    mailbox* mailbox = &mailboxes[mid];
    
    if(message == NULL){
        return(NULL);
    }
    
    message->previous = mailbox->tail;
    message->next = NULL;
    
    /* If this is the first message in the mailbox, point the head to it. */
    if(mailbox->head == NULL){
        mailbox->head = message;
    }
    
    /* If this is not the first message, make the previous tail point to the new message. */
    else{
        mailbox->tail->next = message;
    }
    
    mailbox->tail = message;
    return(message);
}



message* dequeue(int mid){
    mailbox* mailbox = &mailboxes[mid];
    message* message;
    
    /* If the mailbox has only one message, we need to point the tail to 0 as well. */
    if(mailbox->head == mailbox->tail){
        mailbox->tail = NULL;
    }
    
    /* Remove the message from the mailbox. */
    message = mailbox->head;
    mailbox->head = mailbox->head->next;
    return(message);
}



int isEmptyMailbox(int mid){
    mailbox* mailbox = &mailboxes[mid];
    
    /* If the target mailbox is not valid, return -1. */
    if(initializedMailboxes[mid] != 1){
        return(-1);
    }
    
    /* If the mailbox is NULL, return 1. */
    if(mailbox->head == NULL){
        return(1);
    }
    
    /* If the mailbox isn't empty, return 0. */
    return(0);
}



message* createMessage(int sid, int did, char* new_message){
    message* newMessage;                 /* The message we will be creating. */
    newMessage = getMessageBlock();
    if(newMessage != NULL){
        newMessage->source = sid;
        newMessage->destination = did;
        if(Stringcopy(new_message, newMessage->message) == 0){
            return(newMessage);
        }
        else return(NULL);
    }
    return(NULL);
}



message* getMessageBlock(){
    message* temp;
    
    /* Returns a NULL pointer if there's no available message block in the freelist. */ 
    if(fl.length == 0){
        return(NULL);
    }
    
    temp = fl.head;                      /* Get the oldest element in the freelist. */
    fl.head = fl.head->next;             /* Adjust the head pointer of the freelist. */ 
    temp->next = NULL;                   /* Break the connection between the previous head element and the current one. */
    
    /* Adjust the tail pointer of the freelist if there's only one message block in the freelist. */
    if(fl.length == 1){
        fl.head = NULL;
        fl.tail = NULL;
    }
    
    fl.length--;                         /* Decrement the length of the freelist. */
    return(temp);
}



void freeMessageBlock(message* target){
    /* Only free it if the message block is not NULL. */
    if(target != NULL){
        if(fl.length == 0){
            fl.head = target;
        }
        else{
            fl.tail->next = target;          /* Append the message block to the end of the freelist. */
        }
        fl.tail = target;                    
        fl.tail->next = NULL;
        fl.length++;                         /* Increment the length of the freelist. */ 
    }
}


int sendMessage(int sid, int did, char* new_message){
    message* newMessage = NULL;
    
    /* Check if the source and destination are valid. */
    if(initializedMailboxes[sid] == 0 || initializedMailboxes[did] == 0 || sid == did){
        return(-1);
    }
    
    newMessage = createMessage(sid, did, new_message);
    
    if(enqueue(did, newMessage) == NULL)
    {
        return(-2);
    }
    else{
        return(0);
    }
}



int retrieveMessage(int did){
    message* newMessage = NULL;
    
    /* Check if the destination is valid. */
    if(initializedMailboxes[did] == 0){
        return(-1);
    }
    
    /* Check if there's any messages in the destination mailbox. */
    if(isEmptyMailbox(did) == 1){
        return(-2);
    }
    
    newMessage = dequeue(did);
    mailboxes[did].last_message.source = newMessage->source;
    mailboxes[did].last_message.destination = newMessage->destination;
    Stringcopy(newMessage->message, mailboxes[did].last_message.message);  
    freeMessageBlock(newMessage);
    
    return(0);
}


int Stringcopy(char* string1, char* string2){
    int i = 0;
    while(string1[i] != '\0' && i <= 98){
        string2[i] = string1[i];
        i++;
    }
    if(i >= 99){
        return(-1);
    }
    string2[i] = '\0';
    return(0);
}